/* ===================================
Michal Kowalczyk
2010 EZI
==================================== */

// mos3d.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "engine.h"
#include "mos3d.h"
#include "views.h"
#include "processes.h"
//extern int cudafunc();

// =====================================================
// EVENT MANAGER
// =====================================================


EventManager * EventManager::_instance = 0;

EventManager & EventManager::Instance() 
{
	if(_instance == 0 )
		_instance = new EventManager();
	return *_instance;
}

bool EventManager::setup()
{
	// register general view system events
	boost::shared_ptr<ViewListener> viewListener(new ViewListener());
	if(Core::getSettings().video_output)
		newActorSignal.connect(bind(&ViewListener::handleNewActorSignal, viewListener, _1));
	actorMovedSignal.connect(bind(&ViewListener::handleActorMovedSignal, viewListener, _1));

	// register renderer system events
	if(Core::getSettings().video_output)
	{
		boost::shared_ptr<RendererListener> rendererListener(new RendererListener());
		keyPressedSignal.connect(1, bind(&RendererListener::handleKeyPressedSignal, rendererListener, _1));
	}
	// register camera  events
	boost::shared_ptr<CameraListener> cameraListener(new CameraListener());
	keyPressedSignal.connect(bind(&CameraListener::handleKeyPressedSignal, cameraListener, _1));

	// register logic system events	
	boost::shared_ptr<LogicListener> logicListener(new LogicListener());
	collisionSignal.connect(bind(&LogicListener::handleCollisionSignal, logicListener, _1, _2));
	newMoveSignal.connect(bind(&LogicListener::handleNewMoveSignal, logicListener, _1, _2));
	keyPressedSignal.connect(0, bind(&LogicListener::handleKeyPressedSignal, logicListener, _1));

	return true;
}

void EventManager::sendNewActor(EntityID id)
{
	newActorSignal(id);
}
void EventManager::sendKeyPressed(char key)
{
	keyPressedSignal(key);
}

CollisionInfo EventManager::sendActorMoved(int id)
{
	return actorMovedSignal(id);
}

void EventManager::sendCollision(int id, int secondID)
{
	collisionSignal(id, secondID);
}

void EventManager::sendNewMove(int id, int region)
{
	newMoveSignal(id, region);
}


// =====================================================
// MAIN LOGIC
// =====================================================
BaseLogic * BaseLogic::_instance = 0;

BaseLogic & BaseLogic::Instance() 
{
	if(_instance == 0 )
		_instance = new BaseLogic();

	return *_instance;
}
CUresult BaseLogic::CleanupNoFailure()
{
	CUresult error;

	// Free device memory
	if (d_x)
		error = cuMemFree(d_x);
	if (d_destX)
		error = cuMemFree(d_destX);
	if (d_rx)
		error = cuMemFree(d_rx);
	if (d_vx)
		error = cuMemFree(d_vx);

	if (d_z)
		error = cuMemFree(d_z);
	if (d_destZ)
		error = cuMemFree(d_destZ);
	if (d_rz)
		error = cuMemFree(d_rz);
	if (d_vz)
		error = cuMemFree(d_vz);
	// Free host memory
	/*if (h_A)
	free(h_A);
	if (h_B)
	free(h_B);
	if (h_C)
	free(h_C);*/

	error = cuCtxDetach(cuContext);

	return error;
}
void BaseLogic::Cleanup(bool noError)
{
	CUresult error;
	error = CleanupNoFailure();

	if (!noError || error != CUDA_SUCCESS)
		printf("Function call failed\nFAILED\n");


	exit(0);
}

void BaseLogic::setSpeed(float v)
{
	for(int i = 0; i < e; i++)
	{
		vx[i] = v;
		vz[i] = v;
	}
	/*int tmpOff1 = off1;
	int tmpOff2 = off2;

	error = cuParamSetf(parMoveX, tmpOff1, v);
	if (error != CUDA_SUCCESS) Cleanup(false);
	tmpOff1 += sizeof(v);
	error = cuParamSetSize(parMoveX, tmpOff1);
	if (error != CUDA_SUCCESS) Cleanup(false);

	error = cuParamSetf(parMoveZ, tmpOff2, v);
	if (error != CUDA_SUCCESS) Cleanup(false);
	tmpOff2 += sizeof(v);
	error = cuParamSetSize(parMoveZ, tmpOff2);
	if (error != CUDA_SUCCESS) Cleanup(false);*/
}

void BaseLogic::updateStreams()
{
	// Copy vectors from host memory to device memory
	error = cuMemcpyHtoD(d_x, x, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemcpyHtoD(d_destX, destX, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemcpyHtoD(d_rx, rx, n*sizeof(int));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemcpyHtoD(d_vx, vx, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);

	error = cuMemcpyHtoD(d_z, z, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemcpyHtoD(d_destZ, destZ, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemcpyHtoD(d_rz, rz, n*sizeof(int));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemcpyHtoD(d_vz, vz, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
}
bool BaseLogic::startCUDA()
{
	devID = 0;
	// Initialize
	error = cuInit(0);
	if (error != CUDA_SUCCESS){ Cleanup(false); return false; };

	// pick up device with zero ordinal (default, or devID)
	error = cuDeviceGet(&cuDevice, devID);
	if (error != CUDA_SUCCESS){ Cleanup(false); return false; };

	// Create context
	error = cuCtxCreate(&cuContext, 0, cuDevice);
	if (error != CUDA_SUCCESS){ Cleanup(false); return false; };

	error = cuModuleLoad(&cuModule, "parmove.ptx");

	if (error != CUDA_SUCCESS){ Cleanup(false); return false; };

	// Get function handle from module
	error = cuModuleGetFunction(&parMoveX, cuModule, "ParMoveX");
	if (error != CUDA_SUCCESS) { Cleanup(false); return false; };
	// Get function handle from module
	error = cuModuleGetFunction(&parMoveZ, cuModule, "ParMoveZ");
	if (error != CUDA_SUCCESS) { Cleanup(false); return false; };

	// Allocate vectors in device memory
	error = cuMemAlloc(&d_x, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemAlloc(&d_destX, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemAlloc(&d_rx, n*sizeof(int));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemAlloc(&d_vx, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);

	error = cuMemAlloc(&d_z, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemAlloc(&d_destZ, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemAlloc(&d_rz, n*sizeof(int));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemAlloc(&d_vz, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);

	// Invoke kernels
#define ALIGN_UP(offset, alignment) \
	(offset) = ((offset) + (alignment) - 1) & ~((alignment) - 1)
	int offset = 0;

	error = cuParamSetv(parMoveX, offset, &d_x, sizeof(d_x));
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(d_x);
	error = cuParamSetv(parMoveX, offset, &d_destX, sizeof(d_destX));
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(d_destX);
	error = cuParamSetv(parMoveX, offset, &d_rx, sizeof(d_rx));
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(d_rx);
	error = cuParamSetv(parMoveX, offset, &d_vx, sizeof(d_vx));
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(d_vx);

	ALIGN_UP(offset, __alignof(n));
	error = cuParamSeti(parMoveX, offset, n);
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(n);
	error = cuParamSetSize(parMoveX, offset);
	if (error != CUDA_SUCCESS) Cleanup(false);

	/*ALIGN_UP(offset, __alignof(v));
	off1 = offset;
	error = cuParamSetf(parMoveX, offset, v);
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(v);
	error = cuParamSetSize(parMoveX, offset);
	if (error != CUDA_SUCCESS) Cleanup(false);*/


	offset = 0;
	error = cuParamSetv(parMoveZ, offset, &d_z, sizeof(d_z));
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(d_z);
	error = cuParamSetv(parMoveZ, offset, &d_destZ, sizeof(d_destZ));
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(d_destZ);
	error = cuParamSetv(parMoveZ, offset, &d_rz, sizeof(d_rz));
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(d_rz);
	error = cuParamSetv(parMoveZ, offset, &d_vz, sizeof(d_vz));
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(d_vz);

	ALIGN_UP(offset, __alignof(n));
	error = cuParamSeti(parMoveZ, offset, n);
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(n);
	error = cuParamSetSize(parMoveZ, offset);
	if (error != CUDA_SUCCESS) Cleanup(false);

	/*ALIGN_UP(offset, __alignof(v));
	off2 = offset;
	error = cuParamSetf(parMoveZ, offset, v);
	if (error != CUDA_SUCCESS) Cleanup(false);
	offset += sizeof(v);
	error = cuParamSetSize(parMoveZ, offset);
	if (error != CUDA_SUCCESS) Cleanup(false);*/


	threadsPerBlock = 256;
	blocksPerGrid = (n + threadsPerBlock - 1) / threadsPerBlock;
	error = cuFuncSetBlockShape(parMoveX, threadsPerBlock, 1, 1);
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuFuncSetBlockShape(parMoveZ, threadsPerBlock, 1, 1);
	if (error != CUDA_SUCCESS) Cleanup(false);


	return true;
}

bool BaseLogic::setup()
{	

	startCUDA();


	eventManager.setup();
	srand ( time(NULL) );
	// create new data models

	invlay = boost::shared_ptr<InversionLayer>(new InversionLayer(1));
	entityManager.addEntity(1, invlay);
	entityManager.getEntity(1)->setPosition3D( 38.0f, 2.0f, 25.0f);
	eventManager.sendNewActor(1);	


	source.x[0] = Renderer::Instance().getMOS_Structure()->source.x[0];
	source.x[1] = Renderer::Instance().getMOS_Structure()->source.x[1];
	source.z[0] = Renderer::Instance().getMOS_Structure()->source.z[0];
	source.z[1] = Renderer::Instance().getMOS_Structure()->source.z[1];

	ndoped_src.x[0] = Renderer::Instance().getMOS_Structure()->ndoped_src.x[0];
	ndoped_src.x[1] = Renderer::Instance().getMOS_Structure()->ndoped_src.x[1];
	ndoped_src.z[0] = Renderer::Instance().getMOS_Structure()->ndoped_src.z[0];
	ndoped_src.z[1] = Renderer::Instance().getMOS_Structure()->ndoped_src.z[1];

	gate.x[0] = Renderer::Instance().getMOS_Structure()->gate.x[0];
	gate.x[1] = Renderer::Instance().getMOS_Structure()->gate.x[1];
	gate.z[0] = Renderer::Instance().getMOS_Structure()->gate.z[0];
	gate.z[1] = Renderer::Instance().getMOS_Structure()->gate.z[1];

	channel.x[0] = Renderer::Instance().getMOS_Structure()->channel.x[0];
	channel.x[1] = Renderer::Instance().getMOS_Structure()->channel.x[1];
	channel.z[0] = Renderer::Instance().getMOS_Structure()->channel.z[0];
	channel.z[1] = Renderer::Instance().getMOS_Structure()->channel.z[1];

	drain.x[0] = Renderer::Instance().getMOS_Structure()->drain.x[0];
	drain.x[1] = Renderer::Instance().getMOS_Structure()->drain.x[1];
	drain.z[0] = Renderer::Instance().getMOS_Structure()->drain.z[0];
	drain.z[1] = Renderer::Instance().getMOS_Structure()->drain.x[1];

	ndoped_drn.x[0] = Renderer::Instance().getMOS_Structure()->ndoped_drn.x[0];
	ndoped_drn.x[1] = Renderer::Instance().getMOS_Structure()->ndoped_drn.x[1];
	ndoped_drn.z[0] = Renderer::Instance().getMOS_Structure()->ndoped_drn.z[0];
	ndoped_drn.z[1] = Renderer::Instance().getMOS_Structure()->ndoped_drn.z[1];

	under_channel.x[0] = Renderer::Instance().getMOS_Structure()->under_channel.x[0];
	under_channel.x[1] = Renderer::Instance().getMOS_Structure()->under_channel.x[1];
	under_channel.z[0] = Renderer::Instance().getMOS_Structure()->under_channel.z[0];
	under_channel.z[1] = Renderer::Instance().getMOS_Structure()->under_channel.z[1];

	pdoped.x[0] = Renderer::Instance().getMOS_Structure()->pdoped.x[0];
	pdoped.x[1] = Renderer::Instance().getMOS_Structure()->pdoped.x[1];
	pdoped.z[0] = Renderer::Instance().getMOS_Structure()->pdoped.z[0];
	pdoped.z[1] = Renderer::Instance().getMOS_Structure()->pdoped.z[1];


	for(int i = 2; i < (e/2)+2; i++)
	{
		boost::shared_ptr<Electron> el(new Electron(i, 0));
		entityManager.addEntity(i, el);
		float tx = (float) ( rand() % (int)ndoped_drn.x[1] + (int)ndoped_src.x[0]);
		float tz = (float) ( rand() % (int)ndoped_drn.z[1] + (int)ndoped_src.z[0]);
		float nx = (float) ( rand() % (int)ndoped_drn.x[1] + (int)ndoped_src.x[0]);
		float nz = (float) ( rand() % (int)ndoped_drn.z[1] + (int)ndoped_src.z[0]);
		x[i-2] = tx;
		z[i-2] = tz;
		rx[i-2] = 0;
		rz[i-2] = 0;
		destX[i-2] = nx;
		destZ[i-2] = nz;
		entityManager.getEntity(i)->setPosition3D(tx, tz);
		eventManager.sendNewActor(i);
		//eventManager.sendNewMove(i, 0);
	}
	for(int i = (e/2)+2; i < (e+2); i++)
	{
		boost::shared_ptr<Electron> el(new Electron(i, 1));
		entityManager.addEntity(i, el);
		float tx = (float) ( rand() % (int)ndoped_src.x[1] + (int)ndoped_drn.x[0]);
		float tz = (float) ( rand() % (int)ndoped_src.z[1] + (int)ndoped_drn.z[0]);
		float nx = (float) ( rand() % (int)ndoped_src.x[1] + (int)ndoped_drn.x[0]);
		float nz = (float) ( rand() % (int)ndoped_src.z[1] + (int)ndoped_drn.z[0]);
		x[i-2] = tx;
		z[i-2] = tz;
		rx[i-2] = 0;
		rz[i-2] = 0;
		destX[i-2] = nx;
		destZ[i-2] = nz;
		entityManager.getEntity(i)->setPosition3D(tx, tz);
		eventManager.sendNewActor(i);
		//eventManager.sendNewMove(i, 1);
	}
	for(int i = (e+2); i < e+(h/4)+2; i++)
	{
		boost::shared_ptr<Hole> el(new Hole(i, 3));
		entityManager.addEntity(i, el);
		float tx = (float) ( rand() % (int)channel.x[1] + (int)channel.x[0]);
		float tz = (float) ( rand() % (int)channel.z[1] + (int)channel.z[0]);
		float nx = (float) ( rand() % (int)channel.x[1] + (int)channel.x[0]);
		float nz = (float) ( rand() % (int)channel.z[1] + (int)channel.z[0]);
		x[i-2] = tx;
		z[i-2] = tz;
		rx[i-2] = 0;
		rz[i-2] = 0;
		destX[i-2] = nx;
		destZ[i-2] = nz;
		entityManager.getEntity(i)->setPosition3D(tx, tz);
		eventManager.sendNewActor(i);
		//eventManager.sendNewMove(i, 1);
	}
	for(int i = e+(h/4)+2; i < e+(h/2)+2; i++)
	{
		boost::shared_ptr<Hole> el(new Hole(i, 3));
		entityManager.addEntity(i, el);
		float tx = (float) ( rand() % (int)under_channel.x[1] + (int)under_channel.x[0]);
		float tz = (float) ( rand() % (int)under_channel.z[1] + (int)under_channel.z[0]);
		float nx = (float) ( rand() % (int)under_channel.x[1] + (int)under_channel.x[0]);
		float nz = (float) ( rand() % (int)under_channel.z[1] + (int)under_channel.z[0]);
		x[i-2] = tx;
		z[i-2] = tz;
		rx[i-2] = 0;
		rz[i-2] = 0;
		destX[i-2] = nx;
		destZ[i-2] = nz;
		entityManager.getEntity(i)->setPosition3D(tx, tz);
		eventManager.sendNewActor(i);
		//eventManager.sendNewMove(i, 1);
	}
	for(int i = e+(h/2)+2; i < e+h+2; i++)
	{
		boost::shared_ptr<Hole> el(new Hole(i, 3));
		entityManager.addEntity(i, el);
		float tx = (float) ( rand() % (int)pdoped.x[1] + (int)pdoped.x[0]);
		float tz = (float) ( rand() % (int)pdoped.z[1] + (int)pdoped.z[0]);
		float nx = (float) ( rand() % (int)pdoped.x[1] + (int)pdoped.x[0]);
		float nz = (float) ( rand() % (int)pdoped.z[1] + (int)pdoped.z[0]);
		x[i-2] = tx;
		z[i-2] = tz;
		rx[i-2] = 0;
		rz[i-2] = 0;
		destX[i-2] = nx;
		destZ[i-2] = nz;
		entityManager.getEntity(i)->setPosition3D(tx, tz);
		eventManager.sendNewActor(i);
		//eventManager.sendNewMove(i, 1);
	}
	// Copy vectors from host memory to device memory
	updateStreams();


	if(Core::getSettings().text_output)
		printf("Main logic system setup: COMPLETED\nRunning...\n");
	return true;
}

float BaseLogic::current(int ugs, int uds)
{
	if( (uds == 0) || (ugs < 3) )
	{
		state = 0;
		return 1.0f;
	}
	if( ugs == 3)
	{
		if(uds < 3 )
		{
			state = 1;
			return (float)uds;
		}
		else
		{
			state = 2;
			return 2 + (float)(uds*0.1);
		}
	}
	else if( (ugs > 3) && (ugs <= 5) )
	{
		if(uds < 4 )
		{
			state = 1;
			return (float)uds;
		}
		else
		{
			state = 2;
			return 4 + (float)(uds*0.1);
		}
	}
	else if( (ugs > 5) && (ugs <= 8) )
	{
		if(uds < 5 )
		{
			state = 1;
			return (float)uds;
		}
		else
		{
			state = 2;
			return 5 + (float)(uds*0.1);
		}
	}
	else if( (ugs > 8) && (ugs <= 10) )
	{
		if(uds < 7 )
		{
			state = 1;
			return (float)uds;
		}
		else
		{
			state = 2;
			return 7 + (float)(uds*0.1);
		}
	}

	else return 1.0f;
}
void BaseLogic::update()
{

	error = cuLaunchGrid(parMoveX, blocksPerGrid, 1);
	if (error != CUDA_SUCCESS) Cleanup(false);

	error = cuLaunchGrid(parMoveZ, blocksPerGrid, 1);
	if (error != CUDA_SUCCESS) Cleanup(false);

#ifdef _DEBUG
	error = cuCtxSynchronize();
	if (error != CUDA_SUCCESS) Cleanup(false);
#endif

	// Copy result from device memory to host memory
	// h_C contains the result in host memory
	error = cuMemcpyDtoH(x, d_x, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemcpyDtoH(rx, d_rx, n*sizeof(int));
	if (error != CUDA_SUCCESS) Cleanup(false);

	error = cuMemcpyDtoH(z, d_z, n*sizeof(float));
	if (error != CUDA_SUCCESS) Cleanup(false);
	error = cuMemcpyDtoH(rz, d_rz, n*sizeof(int));
	if (error != CUDA_SUCCESS) Cleanup(false);

	float nx = 0.0f;
	float nz = 0.0f;

	if( (Vgs >= 3) && ( Vds > 0 ) )
	{
		isCurrent = true;
		for(int i = 0; i < (e/2); i++)
		{
			if((rx[i] == 1) && (rz[i] == 1))
			{
				switch(phase[i])
				{
				case 0:
					nx = (float) ( rand() % (int)channel.x[1] + (int)channel.x[0]);
					nz = (float) ( rand() % (int)channel.z[1] + (int)channel.z[0]);
					phase[i] = 1;
					break;
				case 1:
					nx = (float) ( rand() % (int)ndoped_drn.x[1] + (int)ndoped_drn.x[0]);
					nz = (float) ( rand() % (int)(ndoped_drn.z[1] - currWidthN ) + (int)ndoped_drn.z[0]);
					phase[i] = 2;
					break;
					/*case 2:
					nx = (float) ( rand() % (int)ndoped_src.x[1] + (int)ndoped_drn.x[0]);
					nz = (float) ( rand() % (int)ndoped_src.z[1] + (int)ndoped_drn.z[0]);
					phase[i] = 3;
					break;*/
				case 2:
					nx = (float) ( rand() % (int)drain.x[1] + (int)drain.x[0]);
					nz = (float) ( rand() % (int)drain.z[1] + (int)drain.z[0]);
					phase[i] = 3;
					break;
				case 3:
					x[i] = (float) ( rand() % (int)source.x[1] + (int)source.x[0]);
					z[i] = (float) ( rand() % (int)source.z[1] + (int)source.z[0]);
					nx = (float) ( rand() % (int)ndoped_src.x[1] + (int)ndoped_src.x[0]);
					nz = (float) ( rand() % (int)(ndoped_src.z[1] - currWidthN)  + (int)ndoped_src.z[0]);
					phase[i] = 0;
					break;
				default:
					nx = (float) ( rand() % (int)ndoped_src.x[1] + (int)ndoped_src.x[0]);
					nz = (float) ( rand() % (int)ndoped_src.z[1] + (int)ndoped_src.z[0]);
					break;
				}
				destX[i] = nx;
				destZ[i] = nz;
				rx[i] = 0;
				rz[i] = 0;
			}
		}
		for(int i = (e/2); i < e; i++)
		{
			if((rx[i] == 1) && (rz[i] == 1) )
			{
				switch(phase[i])
				{
				case 0:
					nx = (float) ( rand() % (int)drain.x[1] + (int)drain.x[0]);
					nz = (float) ( rand() % (int)drain.z[1] + (int)drain.z[0]);
					phase[i] = 1;
					break;
				case 1:
					x[i] = (float) ( rand() % (int)source.x[1] + (int)source.x[0]);
					z[i] = (float) ( rand() % (int)source.z[1] + (int)source.z[0]);
					nx = (float) ( rand() % (int)ndoped_src.x[1] + (int)ndoped_src.x[0]);
					nz = (float) ( rand() % (int)(ndoped_src.z[1] - currWidthN) + (int)ndoped_src.z[0]);
					phase[i] = 2;
					break;
				case 2:
					nx = (float) ( rand() % (int)channel.x[1] + (int)channel.x[0]);
					nz = (float) ( rand() % (int)channel.z[1] + (int)channel.z[0]);
					phase[i] = 3;
					break;
				case 3:
					nx = (float) ( rand() % (int)ndoped_drn.x[1] + (int)ndoped_drn.x[0]);
					nz = (float) ( rand() % (int)(ndoped_drn.z[1] - currWidthN) + (int)ndoped_drn.z[0]);
					phase[i] = 0;
					break;
					/*case 4:
					nx = (float) ( rand() % (int)ndoped_src.x[1] + (int)ndoped_drn.x[0]);
					nz = (float) ( rand() % (int)ndoped_src.z[1] + (int)ndoped_drn.z[0]);
					phase[i] = 0;
					break;*/
				}
				destX[i] = nx;
				destZ[i] = nz;
				rx[i] = 0;
				rz[i] = 0;
			}
		}
	}
	else
	{
		if(isCurrent)
		{
			for(int i = 0; i < e/2; i++)
			{		
				x[i] = (float) ( rand() % (int)ndoped_src.x[1] + (int)ndoped_src.x[0]);
				z[i] = (float) ( rand() % (int)ndoped_src.z[1] + (int)ndoped_src.z[0]);
				rx[i] = 1;
				rz[i] = 1;
				phase[i] = 0;
			}
			for(int i = e/2; i < e; i++)
			{
				x[i] = (float) ( rand() % (int)ndoped_drn.x[1] + (int)ndoped_drn.x[0]);
				z[i] = (float) ( rand() % (int)ndoped_drn.z[1] + (int)ndoped_drn.z[0]);
				rx[i] = 1;
				rz[i] = 1;
				phase[i] = 0;
			}
			isCurrent = false;
		}
		for(int i = 0; i < e/2; i++)
		{
			if((rx[i] == 1) && (rz[i] == 1) )
			{
				nx = (float) ( rand() % (int)ndoped_src.x[1] + (int)ndoped_src.x[0]);
				nz = (float) ( rand() % (int)ndoped_src.z[1] + (int)ndoped_src.z[0]);
				destX[i] = nx;
				destZ[i] = nz;
				rx[i] = 0;
				rz[i] = 0;
			}
		}
		for(int i = e/2; i < e; i++)
		{
			if((rx[i] == 1) && (rz[i] == 1) )
			{
				nx = (float) ( rand() % (int)ndoped_drn.x[1] + (int)ndoped_drn.x[0]);
				nz = (float) ( rand() % (int)ndoped_drn.z[1] + (int)ndoped_drn.z[0]);
				destX[i] = nx;
				destZ[i] = nz;
				rx[i] = 0;
				rz[i] = 0;
			}
		}

	}

	if( (Vgs >= 2 ) && (Vgs < 5) )
	{
		for(int i = e; i < e+h/4; i++)
		{
			if((rx[i] == 1) && (rz[i] == 1) )
			{
				nx = (float) ( rand() % (int)under_channel.x[1] + (int)under_channel.x[0]);
				nz = (float) ( rand() % (int)under_channel.z[1] + (int)under_channel.z[0]);
				destX[i] = nx;
				destZ[i] = nz;
				rx[i] = 0;
				rz[i] = 0;
			}

		}
		for(int i = e+h/4; i < e+h; i++)
		{
			if((rx[i] == 1) && (rz[i] == 1) )
			{
				nx = (float) ( rand() % (int)pdoped.x[1] + (int)pdoped.x[0]);
				nz = (float) ( rand() % (int)pdoped.z[1] + (int)pdoped.z[0]);
				destX[i] = nx;
				destZ[i] = nz;
				rx[i] = 0;
				rz[i] = 0;
			}
		}
	}
	else if( Vgs >= 5)
	{
		for(int i = e; i < e+h; i++)
		{
			if((rx[i] == 1) && (rz[i] == 1) )
			{
				nx = (float) ( rand() % (int)pdoped.x[1] + (int)pdoped.x[0]);
				nz = (float) ( rand() % (int)pdoped.z[1] + (int)pdoped.z[0]);
				destX[i] = nx;
				destZ[i] = nz;
				rx[i] = 0;
				rz[i] = 0;
			}
		}
	}
	else 
	{
		for(int i = e; i < e+h/4; i++)
		{
			if((rx[i] == 1) && (rz[i] == 1) )
			{
				nx = (float) ( rand() % (int)channel.x[1] + (int)channel.x[0]);
				nz = (float) ( rand() % (int)channel.z[1] + (int)channel.z[0]);
				destX[i] = nx;
				destZ[i] = nz;
				rx[i] = 0;
				rz[i] = 0;
			}

		}
		for(int i = e+h/4; i < e+h/2; i++)
		{
			if((rx[i] == 1) && (rz[i] == 1) )
			{
				nx = (float) ( rand() % (int)under_channel.x[1] + (int)under_channel.x[0]);
				nz = (float) ( rand() % (int)under_channel.z[1] + (int)under_channel.z[0]);
				destX[i] = nx;
				destZ[i] = nz;
				rx[i] = 0;
				rz[i] = 0;
			}
		}
		for(int i = e+h/2; i < e+h; i++)
		{
			if((rx[i] == 1) && (rz[i] == 1) )
			{
				nx = (float) ( rand() % (int)pdoped.x[1] + (int)pdoped.x[0]);
				nz = (float) ( rand() % (int)pdoped.z[1] + (int)pdoped.z[0]);
				destX[i] = nx;
				destZ[i] = nz;
				rx[i] = 0;
				rz[i] = 0;
			}

		}
	}
	setSpeed(current(Vgs, Vds));

	for(int i = 2; i < (n+2); i++)
		entityManager.getEntity(i)->setPosition3D(x[i-2], z[i-2]);

	updateStreams();


}

void BaseLogic::increaseVgs() { if(Vgs < 10 ) { Vgs += 1; invlay->setWidth(2.0f * Vgs); }  }
void BaseLogic::decreaseVgs() { if(Vgs > 0 ) { Vgs -= 1; invlay->setWidth(2.0f * Vgs); } }
void BaseLogic::increaseVds() { if(Vds < 16 ) Vds += 1; }
void BaseLogic::decreaseVds() { if(Vds > 0 ) Vds -= 1; }

void LogicListener::handleCollisionSignal(int id, int secondID)
{

}

void LogicListener::handleNewMoveSignal(int id, int region)
{
	switch(region)
	{
	case 0: // ndoped_drn
		newX = (float) ( rand() % (int)Renderer::Instance().getMOS_Structure()->ndoped_drn.x[1] + (int)Renderer::Instance().getMOS_Structure()->ndoped_drn.x[0]);
		newZ = (float) ( rand() % (int)Renderer::Instance().getMOS_Structure()->ndoped_drn.z[1] + (int)Renderer::Instance().getMOS_Structure()->ndoped_drn.z[0]);
		break;
	case 1: // ndoped_src
		newX = (float) ( rand() % (int)Renderer::Instance().getMOS_Structure()->ndoped_src.x[1] + (int)Renderer::Instance().getMOS_Structure()->ndoped_src.x[0]);
		newZ = (float) ( rand() % (int)Renderer::Instance().getMOS_Structure()->ndoped_src.z[1] + (int)Renderer::Instance().getMOS_Structure()->ndoped_src.z[0]);
		break;
	default:
		newX = rand() % mapWidth;
		newZ = rand() % mapHeight;
		break;
	}
	ProcessPtr move(new MoveTo(id, (float)newX, (float)newZ, true));
	processManager.attach(move);	
}

void LogicListener::handleKeyPressedSignal(char key)
{
	switch(key)
	{
	case 'u':
		BaseLogic::Instance().increaseVds();
		break;
	case 'j':
		BaseLogic::Instance().decreaseVds();
		break;
	case 'i':
		BaseLogic::Instance().increaseVgs();
		break;
	case 'k':
		BaseLogic::Instance().decreaseVgs();
		break;
	default:
		break;
	}
	BaseLogic::Instance().updateState();
}
// =====================================================
// RENDERER
// =====================================================
bool Camera::releaseCursor(false);
bool Camera::autoMode(true);
inline Camera::Camera(): x(0.0f), y(280.0f), z(0.0f), \
	hangle(0.0f), vangle(0.0f), speed(0.1f), mouseSpeed(0.2f), dir(1) { }
void Camera::setup(float _speed, float _mousespeed, MOS_Structure * _mos_structure, bool * keys)
{
	speed = _speed;
	mouseSpeed = _mousespeed;
	mos_structure =  _mos_structure;
	x = (float)(mos_structure->getxMapSize())/2;
	z = (float)(-(mos_structure->getzMapSize())/2);
	m_keys = keys;
}
void Camera::setup(float _speed, float _mousespeed, float startx, float startz, MOS_Structure * _mos_structure, bool * keys)
{
	speed = _speed;
	mouseSpeed = _mousespeed;
	mos_structure = _mos_structure;
	x = startx;
	z = -startz;
	m_keys = keys;
}
void CameraListener::handleKeyPressedSignal(char key)
{
	switch(key)
	{
	case 'c':

		if(SDL_ShowCursor(SDL_ENABLE))
		{
			SDL_ShowCursor(SDL_DISABLE);
			SDL_WarpMouse(100, 100);
			Camera::releaseCursor = false;
		}
		else
			Camera::releaseCursor = true;
		break;
	case 'f':
		if(Camera::autoMode)
			Camera::autoMode = false;
		else
			Camera::autoMode = true;
		break;
	default:
		break;
	}
}
void Camera::update()
{
	// direct wsad controller
	/*	if(m_keys['w'])
	{
	x += (float)sin(hangle*3.14f/180.0f)*speed;
	z -= (float)cos(hangle*3.14f/180.0f)*speed;
	//y -= (float)sin(vangle*3.14f/180.0f)*speed*2;
	}

	if(m_keys['s'])
	{
	x -= (float)sin(hangle*3.14f/180.0f)*speed;
	z += (float)cos(hangle*3.14f/180.0f)*speed;
	//y += (float)sin(vangle*3.14f/180.0f)*speed*2;
	}*/
	if(autoMode)
	{
		if( dir )
		{
			x += speed*2;
			if( x > mos_structure->getxMapSize() )
				dir = 0;
		}
		else 
		{
			x -= speed*2;
			if ( x < -mos_structure->getxMapSize()/2 )
				dir = 1;
		}
	}
	else
	{
		if(m_keys['a'])
		{
			//x -= (float)sin((hangle+90.0f)*3.14f/180.0f)*speed;
			x += speed*3;
			//	y += speed*3;
			//	z -= (float)cos((hangle-90.0f)*3.14f/180.0f)*speed;
		}
		if(m_keys['d'])
		{
			//x += (float)sin((hangle-90.0f)*3.14f/180.0f)*speed;
			x -= speed*3;
			//y -= speed*3;
			//	z -= (float)cos((hangle+90.0f)*3.14f/180.0f)*speed;
		}

	}
	/*if(x < 0.0f)
	x = 0.0f;
	else if(x > terrain->getxMapSize()-1)
	x = terrain->getxMapSize()-1;
	if(z < -(terrain->getzMapSize()-1))
	z = -(terrain->getzMapSize()-1);
	else if(z > 0.0f)
	z = 0.0f;*/

	//terrainHeight = terrain->getHeight(x, -z)+5.0f;


	if(m_keys['z'])
		y -= speed*3;
	if(m_keys['x'])
		y += speed*3;

	//	else 
	//	y = terrainHeight; // walk

	//if(!releaseCursor)
	//{
	//	// direct mouse controller
	//	SDL_GetMouseState(&oldx, &oldy);
	//	SDL_WarpMouse(100, 100);

	//	hangle += (float)(oldx-100)*mouseSpeed;
	//	vangle += (float)(oldy-100)*mouseSpeed;

	//	//	if(vangle >= 90.0f)
	//	//		vangle = 90.0f;
	//	//	if(vangle <= -90.0f)
	//	//		vangle = -90.0f;
	//}

	glRotatef(vangle, 1.0f, 0.0f, 0.0f);
	glRotatef(hangle, 0.0f, 1.0f, 0.0f);
	gluLookAt(x, y, -70.0f, mos_structure->getxMapSize()/2, 0.0, -60.0f, 0.0f, 0.0f, 1.0f);
	//gluLookAt(x, y, z, x, y, z-1.0f, 0.0f, 1.0f, 0.0f);
}

bool MOS_Structure::loadHeightMap(std::string filename, float heightFactor)
{
	unsigned int bitformat = 0;
	hfactor = heightFactor;

	SDL_Surface * heightmap = IMG_Load( filename.c_str() );
	if(!heightmap)
	{
		logger.log("failed to load heightmap", 1);
		return false;
	}
	else
	{
		bitformat = heightmap->format->BitsPerPixel;
		if( (bitformat != 8) && (bitformat != 24) )
		{
			logger.log("the bitmap can be only in 8 or 24 bits per pixel format. Current format: ", bitformat, 1);
			SDL_FreeSurface(heightmap);
			return false;
		}
		else
			logger.log("heightmap has been loaded successful", 0);
	}

	width = heightmap->w;
	//	logger.log("map width: ", width, 0); 
	height = heightmap->h;
	//	logger.log("map height: ", height, 0);

	try
	{
		vertices = new vector3D *[width];
	}
	catch (std::bad_alloc& ba)
	{
		logger.log("failed to allocate memory for map vertices. ", 1);
	}

	for(int i = 0; i < width; i++)
	{
		try
		{
			vertices[i] = new vector3D[height];
		}
		catch (std::bad_alloc& ba)
		{
			logger.log("failed to allocate memory for map vertices ", i, 1);
			return false;
		}
	}
	logger.log("memory for map vertices has been allocated", 0);

	unsigned char * data;
	if(bitformat == 24)
		data = new unsigned char[width*height*3];	
	else if(bitformat == 8)
		data = new unsigned char[width*height];

	data = (unsigned char *)heightmap->pixels;
	int counter = 0;
	for(int j = 0; j < height; j++)
	{
		for(int i = 0; i < width; i++)
		{
			vertices[i][j].x = (float)(i);
			vertices[i][j].z = -(float)(j);

			if(bitformat == 24)
			{
				vertices[i][j].y = (float)( ( data[counter] + \
					data[counter+1] + \
					data[counter+2] ) / 3.0f * heightFactor);

				counter += 3;
			}
			else if( bitformat == 8 )
			{
				vertices[i][j].y = (float)( data[counter] * heightFactor);

				counter += 1;
			}
		}
	}
	SDL_FreeSurface(heightmap);
	return true;
}

float MOS_Structure::getHeight(float x, float z)
{
	z = abs(z);
	x = abs(x);
	_x = (int)floor(x);
	_z = (int)floor(z);
	if(_x < 0)
		_x = 0;
	else if(_x > width)
		_x = width - 1;
	if(_z < 0)
		_z = 0;
	else if(_z > height)
		_z = height - 1;

	return vertices[_x][_z].y;
}
void MOS_Structure::drawSquare(int i, int j)
{
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(vertices[i-1][j-1].x, vertices[i-1][j-1].y, vertices[i-1][j-1].z);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3f(vertices[i][j-1].x, vertices[i][j-1].y, vertices[i][j-1].z);
	glTexCoord2f(1.0f, 1.0f);
	glVertex3f(vertices[i][j].x, vertices[i][j].y, vertices[i][j].z);

	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(vertices[i-1][j-1].x, vertices[i-1][j-1].y, vertices[i-1][j-1].z);
	glTexCoord2f(1.0f, 1.0f);
	glVertex3f(vertices[i][j].x, vertices[i][j].y, vertices[i][j].z);
	glTexCoord2f(0.0f, 1.0f);
	glVertex3f(vertices[i-1][j].x, vertices[i-1][j].y, vertices[i-1][j].z);
}
void MOS_Structure::drawSquare(float x1, float x2, float z1, float z2)
{
	float y = 1.0f;
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(x1, y, -z1);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3f(x2, y, -z1);
	glTexCoord2f(1.0f, 1.0f);
	glVertex3f(x2, y, -z2);

	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(x1, y, -z1);
	glTexCoord2f(1.0f, 1.0f);
	glVertex3f(x2, y, -z2);
	glTexCoord2f(0.0f, 1.0f);
	glVertex3f(x1, y, -z2);
}
bool MOS_Structure::loadTextures()
{
	std::string mediapath = Core::getSettings().media_path;
	std::string path;
	SDL_Surface *image;
	glGenTextures(7, texture);

	for(int i = 0; i < 7; i++)
	{
		path.clear();
		path = mediapath;
		path.append(texturesNames[i]);
		image = 0;
		image = IMG_Load(path.c_str());
		if(!image)
		{
			logger.log("failed to load texture file for mos_structure", 1);
			return false;
		}
		else
			logger.log("mos_structure texture file has been loaded: ", i+1, 0);

		glBindTexture(GL_TEXTURE_2D, texture[i]);
		gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, image->w, image->h, GL_RGB, GL_UNSIGNED_BYTE, image->pixels);


	}

	SDL_FreeSurface(image);

	return true;
}

bool MOS_Structure::setup()
{
	std::string mediapath = Core::getSettings().media_path;
	std::string path(mediapath);
	path.append("map.bmp");
	if( !loadHeightMap(path.c_str(), 0.1f) )
		return false;

	if(!Core::getSettings().video_output)
		return true;

	if( ! loadTextures() )
		return false;

	MOS_StructureList = glGenLists(1);

	glNewList(MOS_StructureList, GL_COMPILE);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);

	//glColor3f(1.0f, 1.0f, 1.0f);

	for(int j = 1; j < height-dim.y1[0]; j++)
	{
		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glBegin(GL_TRIANGLES);
		for(int i = 1; i < width-dim.y1[1]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[1]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y1[1]; i < width-dim.y1[2]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y1[2]; i < width-dim.y1[3]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[1]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y1[3]; i < width-dim.y1[4]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y1[4]; i < width-dim.y1[5]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[1]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y1[5]; i < width-dim.y1[6]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y1[6]; i < width; i++)
			drawSquare(i, j);
		glEnd();
	}
	for(int j = height-dim.y1[0]; j < height-dim.y2[0]; j++)
	{
		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glBegin(GL_TRIANGLES);
		for(int i = 1; i < width-dim.y1[1]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[1]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y1[1]; i < width-dim.y1[2]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y1[2]; i < width-dim.y1[5]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[1]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y1[5]; i < width-dim.y1[6]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y1[6]; i < width; i++)
			drawSquare(i, j);
		glEnd();
	}

	for(int j = height-dim.y2[0]; j < height-dim.y3[0]; j++)
	{
		glBindTexture(GL_TEXTURE_2D, texture[2]);
		glBegin(GL_TRIANGLES);
		for(int i = 1; i < width-dim.y2[1]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[3]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y2[1]; i < width-dim.y2[2]; i++)
			drawSquare(i, j);
		glEnd();

		glBindTexture(GL_TEXTURE_2D, texture[2]);
		glBegin(GL_TRIANGLES);
		for(int i = width-dim.y2[2]; i < width; i++)
			drawSquare(i, j);
		glEnd();

	}

	for(int j = height-dim.y3[0]; j < height; j++)
	{
		glBindTexture(GL_TEXTURE_2D, texture[3]);
		glBegin(GL_TRIANGLES);
		for(int i = 1; i < width; i++)
			drawSquare(i, j);
		glEnd();
	}

	glBindTexture(GL_TEXTURE_2D, texture[4]);
	glBegin(GL_TRIANGLES);
	drawSquare(15.0f, 45.0f, 8.0f, 38.0f);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, texture[5]);
	glBegin(GL_TRIANGLES);
	drawSquare(90.0f, 110.0f, 2.0f, 22.0f);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, texture[6]);
	glBegin(GL_TRIANGLES);
	drawSquare(155.0f, 185.0f, 8.0f, 38.0f);
	glEnd();
	glEndList();


	glEndList();


	return true;

}

bool HUD::setup()
{
	color.r = 255;
	color.g = 255;
	color.b = 255;
	x = 10.0f;
	y = Core::getSettings().screen_height-50.0f;

	dsx = x;
	dsy = Core::getSettings().screen_height-160.0f;

	gsx = x;
	gsy = Core::getSettings().screen_height-200.0f;

	sx = x;
	sy = Core::getSettings().screen_height - (Core::getSettings().screen_height - 10.0f);

	std::string mediapath = Core::getSettings().media_path;
	std::string path(mediapath);
	path.append("verdana.ttf");
	if( !(font = TTF_OpenFont(path.c_str(), 16) ) )
		STLogger::Instance().log("failed to open font ",1);
	else
		STLogger::Instance().log("font has been loaded ", 0);

	glGenTextures(3, texture);

	initial = TTF_RenderText_Blended(font, "A 3D N-channel enhancement MOSFET Visualization", color);
	w = (float)nextpoweroftwo(initial->w);
	h = (float)nextpoweroftwo(initial->h);

	intermediary = SDL_CreateRGBSurface(0, (int)w, (int)h, 32, 
		0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
	SDL_BlitSurface(initial, 0, intermediary, 0);

	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glTexImage2D(GL_TEXTURE_2D, 0, 4, (int)w, (int)h, 0, GL_BGRA, 
		GL_UNSIGNED_BYTE, intermediary->pixels );

	updateVgs();
	updateVds();
	updateState();

	return true;
}
void HUD::updateState()
{
	std::string buffer; 
	int state = BaseLogic::Instance().getState();
	switch(state)
	{
	case 0:
		buffer = "State: OFF";
		break;
	case 1:
		buffer = "State: LINEAR";
		break;
	case 2:
		buffer = "State: SATURATION";
		break;
	}
	initial = TTF_RenderText_Blended(font, buffer.c_str(), color);
	ws = (float)nextpoweroftwo(initial->w);
	hs = (float)nextpoweroftwo(initial->h);

	intermediary = SDL_CreateRGBSurface(0, (int)ws, (int)hs, 32, 
		0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
	SDL_BlitSurface(initial, 0, intermediary, 0);

	glBindTexture(GL_TEXTURE_2D, texture[3]);
	glTexImage2D(GL_TEXTURE_2D, 0, 4, (int)ws, (int)hs, 0, GL_BGRA, 
		GL_UNSIGNED_BYTE, intermediary->pixels );
}

void HUD::updateVgs()
{
	std::string buffer; 
	buffer = "Ugs : ";
	std::stringstream  strm;
	strm << BaseLogic::Instance().getVgs();
	buffer.append(strm.str());
	buffer.append(" [V]");

	initial = TTF_RenderText_Blended(font, buffer.c_str(), color);
	wgs = (float)nextpoweroftwo(initial->w);
	hgs = (float)nextpoweroftwo(initial->h);

	intermediary = SDL_CreateRGBSurface(0, (int)wgs, (int)hgs, 32, 
		0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
	SDL_BlitSurface(initial, 0, intermediary, 0);

	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glTexImage2D(GL_TEXTURE_2D, 0, 4, (int)wgs, (int)hgs, 0, GL_BGRA, 
		GL_UNSIGNED_BYTE, intermediary->pixels );
}

void HUD::updateVds()
{
	std::string buffer; 
	buffer = "Uds : ";
	std::stringstream  strm;
	strm << BaseLogic::Instance().getVds();
	buffer.append(strm.str());
	buffer.append(" [V]");

	initial = TTF_RenderText_Blended(font, buffer.c_str(), color);
	wds = (float)nextpoweroftwo(initial->w);
	hds = (float)nextpoweroftwo(initial->h);

	intermediary = SDL_CreateRGBSurface(0, (int)wds, (int)hds, 32, 
		0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
	SDL_BlitSurface(initial, 0, intermediary, 0);

	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glTexImage2D(GL_TEXTURE_2D, 0, 4, (int)wds, (int)hds, 0, GL_BGRA, 
		GL_UNSIGNED_BYTE, intermediary->pixels );
}

void HUD::renderLocation(const float & _x, const float & _y, const float & _w, const float & _h)
{
	/* Draw a quad at location */
	glBegin(GL_QUADS);
	/* Recall that the origin is in the lower-left corner
	That is why the TexCoords specify different corners
	than the Vertex coors seem to. */
	glTexCoord2f(0.0f, 1.0f); 
	glVertex2f(_x, _y);
	glTexCoord2f(1.0f, 1.0f); 
	glVertex2f(_x + _w, _y);
	glTexCoord2f(1.0f, 0.0f); 
	glVertex2f(_x + _w, _y + _h);
	glTexCoord2f(0.0f, 0.0f); 
	glVertex2f(_x , _y + _h);
	glEnd();
}
void HUD::render()
{
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	/* GL_NEAREST looks horrible, if scaled... */
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glEnable2D();
	glDisable(GL_DEPTH_TEST);

	glEnable(GL_TEXTURE_2D);
	glColor3f(255.0f, 255.0f, 255.0f);

	renderLocation(x, y, w, h);

	glBindTexture(GL_TEXTURE_2D, texture[1]);
	/* GL_NEAREST looks horrible, if scaled... */
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	renderLocation(gsx, gsy, wgs, hgs);

	/* GL_NEAREST looks horrible, if scaled... */
	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	renderLocation(dsx, dsy, wds, hds);

	/* GL_NEAREST looks horrible, if scaled... */
	glBindTexture(GL_TEXTURE_2D, texture[3]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	renderLocation(sx, sy, ws, hs);

	/* Come out of HUD mode */
	glEnable(GL_DEPTH_TEST);
	glDisable2D();
	glDisable(GL_TEXTURE_2D);
}



Renderer * Renderer::_instance = 0;
Renderer & Renderer::Instance()
{
	if(_instance == 0 )
		_instance = new Renderer();

	return *_instance;
}
bool Renderer::setup(bool * keys)
{
	m_keys = keys;
	// setup OpenGL
	// TODO clean the mess
	if(Core::getSettings().video_output)
	{
		glEnable( GL_TEXTURE_2D );
		glDisable(GL_LIGHTING);
		glDisable(GL_COLOR_MATERIAL);
		glClearColor(0.0, 0.0, 0.0	, 0);
		glViewport(0, 0, Core::getSettings().screen_width, Core::getSettings().screen_height);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(Core::getSettings().fov,(GLfloat)(Core::getSettings().screen_width)/(GLfloat)(Core::getSettings().screen_height), \
			Core::getSettings().nearcl,Core::getSettings().farcl);

		glMatrixMode(GL_MODELVIEW);										
		glLoadIdentity();		

		glShadeModel(GL_SMOOTH);	

		//glFrontFace(GL_CCW);
		//glCullFace(GL_BACK);
		//glEnable(GL_CULL_FACE);

		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // GL_FASTEST
		glEnable(GL_DEPTH_TEST);						// Enables Depth Testing
		glDepthFunc(GL_LEQUAL);							// The Type Of Depth Testing To Do

		if(Core::getSettings().fog)
		{
			glEnable(GL_FOG);		
			glFogi(GL_FOG_MODE, GL_LINEAR);
			glFogf(GL_FOG_START, Core::getSettings().fogstart);
			glFogf(GL_FOG_END, Core::getSettings().fogend);
			glFogfv(GL_FOG_COLOR, fogColor);
		}

		if(Core::getSettings().blending)
		{
			glAlphaFunc(GL_GREATER, 0.25f);
			//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glBlendFunc(GL_ONE, GL_ONE);
			glEnable(GL_BLEND);
		}
	}
	mos_structure = new MOS_Structure();
	if(Core::getSettings().text_output)
		printf("Terrain has been loaded\n");
	if(mos_structure->setup())
	{
		if(Core::getSettings().video_output)
		{
			camera = new Camera;
			camera->setup(Core::getSettings().cameraSpeed, Core::getSettings().mouseSpeed, 100.0f, 50.0f, mos_structure, m_keys);

			hud = new HUD;
			hud->setup();
		}
		if(Core::getSettings().text_output)
			printf("World has been created\n");
		return true;
	}
	else
		return false;

}

void Renderer::renderAllViews()
{
	ViewMap::iterator i = Instance().m_viewMap.begin();
	ViewMap::iterator end = Instance().m_viewMap.end();

	while( i != end) 
	{
		(*i).second->update();
		(*i).second->render();
		++i;
	}
}


void Renderer::attachView(EntityID id, ViewPtr view)
{
	ViewMap::iterator it = m_viewMap.begin();
	m_viewMap.insert(it, std::pair< EntityID, ViewPtr >(id, view) );
}

void Renderer::update()
{
	if(!Core::getSettings().video_output)
		return;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW); 
	glLoadIdentity();

	camera->update();
	mos_structure->render();

	renderAllViews();

	hud->render();

	SDL_GL_SwapBuffers();
}

void RendererListener::handleKeyPressedSignal(char key)
{
	if( (key == 'u') || (key == 'j') )
		Renderer::Instance().getHUD()->updateVds();
	else if( (key == 'i') || (key == 'k') )
		Renderer::Instance().getHUD()->updateVgs();
	Renderer::Instance().getHUD()->updateState();
}

// =====================================================
// MAIN LOOP
// =====================================================
void MOS3DApp::handleKeyDown(SDL_keysym* keysym)
{
	switch(keysym->sym)
	{
	case SDLK_ESCAPE:
		running = false;
		break;
	case SDLK_c:
		eventManager.sendKeyPressed('c');
		break;
	case SDLK_f:
		eventManager.sendKeyPressed('f');
		break;
	case SDLK_u:
		eventManager.sendKeyPressed('u');
		break;
	case SDLK_j:
		eventManager.sendKeyPressed('j');
		break;
	case SDLK_i:
		eventManager.sendKeyPressed('i');
		break;
	case SDLK_k:
		eventManager.sendKeyPressed('k');
		break;
	default:
		keys[keysym->sym]=1;
		break;
	}
	return;
}


void MOS3DApp::handleKeyUp(SDL_keysym* keysym)
{
	keys[keysym->sym]=0;
	return;
}

void MOS3DApp::run()
{
	if(SDLinit())
	{
		if(renderer.setup(Core::keys))
		{
			if(baseLogic.setup())
			{
				if(Core::getSettings().text_output)
					boost::thread handleConsole(&Core::handleConsoleEvents);
				int FPS = Core::getSettings().fps;
				while(running)
				{
					waitFrame(FPS);
					handleSDLEvents();
					baseLogic.update();
					processManager.updateProcesses();
					renderer.update();	
				}
			}
			else
				logger.log("failed to setup program logic", 1);
		}
		else
			logger.log("failed to setup renderer system", 1);

		if(Core::getSettings().text_output)
			printf("Quitting...\n");
		SDL_Quit();
	}
	else
		logger.log("failed to initializate SDL", 1);

}

// =====================================================
// ENTRY POINT
// =====================================================

#ifdef WIN32
#undef main
#endif
int main(int argc, char* argv[])
{
	using namespace boost::interprocess;

	if ( argc > 1 )
	{
		if(!strncmp(argv[1],"-clear",6) )
		{
			named_mutex::remove("SingleAppInstanceMOS3D");
			return 0;
		}
	}

	try
	{
		named_mutex mutex(create_only, "SingleAppInstanceMOS3D");
	}
	catch(interprocess_exception &ex)
	{
		return 1;
	}

	MOS3DApp mos3dapp("MOS3D", "1.01");
	mos3dapp.run();

	named_mutex::remove("SingleAppInstanceMOS3D");

	return 0;
}

